package miku.firstgame.net
{
	import flash.errors.EOFError;
	import flash.errors.IOError;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.ObjectEncoding;
	import flash.net.Socket;
	import flash.system.Security;
	import flash.utils.ByteArray;
	import flash.utils.clearInterval;
	import flash.utils.clearTimeout;
	import flash.utils.setInterval;

	/**
	 * IConnection 的一个实现，默认实现
	 */
	//在SocketImpl之上应该有一个抽象类
	public class SocketImpl extends Socket implements IConnection
	{
		public static const NAME:String = "socketImpl";

		/**
		 * 请求超时
		 */
		//TODO 要Flash Player 10 Socket才支持timeout属性
		public static const REQUEST_TIMEOUT:int = 20 * 1000;
		public static const PING_INTERVAL_NUM:Number = 17000;

		protected var handler:IConnectionHandler;

		private var packet:ByteArray;

		private var pingIntervalID:int;
		private var timeoutId:int;

//		private var _publicKey:String;
//		private var _hashKey:String;
		private var _startProtocolNum:int = -1;

		public function SocketImpl()
		{
			init();
		}

//		/**
//		 * 请求服务器返回，服务器会返回一个和请求的 XML 协议同名的 XML对象
//		 * @param request 请求服务器的对象
//		 */
//		//TODO 此方法应该作为一个抽象类存在
//		public function requestServer(request:IRequest):void
//		{
//			var data:Object = request.requestData;
//			var requestData:XML = XML(data);
//			send(requestData);
//		}

		/**
		 * 发送数据
		 * @param data xml对象
		 */
		public function send(data:Object):void
		{
			if (!connected)
			{
				trace("sending " + data.name().toString() + " failure.");
				return;
			}
			if (data is XML && (data as XML).name().toString() != "Typing" && (data as XML).name().toString() != "Ping")
			{
				trace("[Send]" + data.toXMLString());
			}

			if (data is XML)
			{
				data = (data as XML).toXMLString();
			}
			var bytes:ByteArray = Encoder.encode(data);
			try
			{
				writeBytes(bytes);
				flush();
			}
			catch (e:IOError)
			{
				trace("[SocketImpl] the packet encounter io error, send failed");
			}
		}

		//可以考虑采用专门的验证服务器
		override public function connect(host:String, port:int):void
		{
			Security.allowDomain("*");
			Security.loadPolicyFile("xmlsocket://" + host + ":" + port); //port 843);
			//trace('Security.loadPolicyFile(' + "xmlsocket://" + host + ":" + port + ')');
			super.connect(host, port);
		}

		/**
		 * 设置连接处理
		 */
		public function setHandler(handler:IConnectionHandler):void
		{
			this.handler = handler;
		}

		//TODO 先改成protected, 便于子类覆盖，减少不必要的事件监听，以后还需要重构
		protected function init():void
		{
			this.objectEncoding = ObjectEncoding.AMF3;

			packet = new ByteArray();
			addEventListener(ProgressEvent.SOCKET_DATA, onData);
			addEventListener(Event.CONNECT, onConnect);
			addEventListener(Event.CLOSE, onClose);
			addEventListener(IOErrorEvent.IO_ERROR, onError);
			addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
		}

		//TODO 这种与协议相关的包可以封装到更合适的地方,这里之所以将此方法公开，就是为了它被加密包装后，可以重新ping
		public function ping(pingFunction:Function = null):void
		{
			var connection:IConnection = this;
			clearInterval(pingIntervalID);
			if (null == pingFunction)
			{
				pingIntervalID = setInterval(function():void
				{
					if (this.connected) //WrappedSock.isLoadInitProtocol)
					{
						connection.send(<Ping/>);
					}

				}, PING_INTERVAL_NUM);
			}
			else
			{
				pingIntervalID = setInterval(pingFunction, PING_INTERVAL_NUM);
			}
		}

		protected function onData(event:ProgressEvent):void
		{
			while (bytesAvailable > 0)
			{
				try
				{
					var c:int = readByte();
					packet.writeByte(c);
					if (c == 0)
					{
						trigger();
						packet = new ByteArray();
					}
				}
				catch (e:EOFError)
				{
					trace("[SocketImpl] the packet encounter eof error, read failed");
					packet = new ByteArray();
					break;
				}
				catch (e:IOError)
				{
					trace("[SocketImpl] the packet encounter io error, read failed");
					packet = new ByteArray();
					break;
				}
			}
		}

		//TODO TODOID201004200007 暗藏严重的Bug, 不能胜任并发IRequest的情况, 以及负载均衡相关的问题
		protected function trigger():void
		{
			var value:String = Decoder.decode(packet);
			var data:XML = new XML(value);
			if (data.name().toString() != "Typing")
			{
				trace("[Get]" + data.toXMLString());
			}
//			var data:String = Decoder.decode(packet);
//			trace("[Get]" + data);
			//dispatchEvent(new XMLPacketEvent(data.name(), data));
			handler.handleData(data, this);
		}

		private function onClose(event:Event):void
		{
			handler.handleClose(this);
			clearInterval(pingIntervalID);
		}

		private function onConnect(event:Event):void
		{
			handler.handleConnect(this);
			ping();
		}

		private function onError(event:Event):void
		{
			trace("Connection error: " + event["text"]);
			handler.handleError(this);
		}

	}
}
